package hikari.managers.undo;

import com.gooddies.wiring.annotations.Wire;
import com.gooddies.wiring.annotations.WiringComponent;
import hikari.utils.Utils;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JComponent;

/**
 * @author sad
 */
@WiringComponent(singleton = true)
public class Undo {

    private static int UNDO_STEPS = 20;
    private boolean inProgress = false;
    private List<WeakReference<JComponent>> undoComponents = new ArrayList<>();
    private List<Undoable> undoables = new LinkedList<>();
    @Wire
    private Utils utils;

    private Undo() {
    }

    public void put(Undoable undoable) {
        undoables.add(undoable);
        enableUndoButtons(true);
        while (undoables.size() > UNDO_STEPS) {
            undoables.remove(0);
        }
    }

    public void clear() {
        undoables.clear();
        enableUndoButtons(false);
    }

    public void undo() {
        try {
            inProgress = true;
            if (!undoables.isEmpty()) {
                try {
                    int lastIndex = undoables.size() - 1;
                    Undoable undo = undoables.get(lastIndex);
                    undoables.remove(lastIndex);
                    undo.undo();
                } catch (Exception ex) {
                    utils.logError(ex);
                    utils.errorMessage("Error while undo. " + ex.getLocalizedMessage());
                }
                if (undoables.isEmpty()) {
                    enableUndoButtons(false);
                }
            }
        } finally {
            inProgress = false;
        }
    }

    private void enableUndoButtons(boolean enable) {
        for (WeakReference<JComponent> comp : undoComponents) {
            if (comp.get() != null) {
                if (comp.get().isEnabled() != enable) {
                    comp.get().setEnabled(enable);
                }
            }
        }
    }

    public void addUndoButton(JComponent component) {
        undoComponents.add(new WeakReference<>(component));
    }

    public void removeUndoButton(JComponent component) {
        WeakReference<JComponent> comp = null;
        for (int i = 0; i < undoComponents.size(); i++) {
            if (comp.get() != null) {
                if (comp.get() == component) {
                    undoComponents.remove(i);
                    break;
                }
            }
        }
    }

    /**
     * Magic object that with reflection gets current method that invokes this method<br>
     * You should invoke it in <b>bean method<b>(ie. setX, setY), removes "set", added "get" or "is",<br>
     * and remember last state.
     */
    public void createPojoUndo(Object objectToUndo) {
        if (inProgress) {
            return;
        }
        try {
            PojoUndoable undoable = new PojoUndoable(objectToUndo);
            put(undoable);
        } catch (Exception ex) {
            utils.logError(ex);
        }
    }
}
